package org.mcflier.appuntamento.tasks;

import java.lang.reflect.InvocationTargetException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;

import org.mcflier.appuntamento.core.Factory;
import org.mcflier.appuntamento.core.exception.ModulesException;
import org.mcflier.appuntamento.core.logger.LoggerManager;
import org.mcflier.appuntamento.core.modules.IModule;
import org.mcflier.appuntamento.core.modules.ModuleNode;

public class TaskManager implements IModule {

        private static final String TASKS_TAG = "tasks";
        private static final String TASK_TAG = "task";
        private static final String NAME_TAG = "name";
        private static final String DELAY_TAG = "delay";
        private static final String PATH_TAG = "path";
        private static final String ARGS_TAG = "args";
        private static final String ARG_TAG = "arg";
        private static final String ARG_NAME_TAG ="name";
        private static final String ARG_VALUE_TAG = "value";

        private Map<String, AbstractTask> tasks;
        private Logger logger;

        public TaskManager() throws ModulesException{
                this.tasks = new HashMap<String, AbstractTask>();
                this.logger = ((LoggerManager)Factory.getInstance().getModuleInstance(Factory.MODULE_LOGGER)).getLogger(this.getClass().getName());
        }

        @Override
        public void receiveModuleContent(ModuleNode node) {
                node = node.getNode(TASKS_TAG);
                if(node.getNodes().size()==0 && node.getAttributes().size()!=0){
                        Set<String> keys = node.getAttributes().keySet();
                        ModuleNode temp = new ModuleNode(TASK_TAG);
                        for (String key : keys) {
                                temp.add(key, node.getAttribute(key));
                        }
                        node.getAttributes().clear();
                        node.add(temp.getName(), temp);
                }

                Set<String> keys = node.getNodes().keySet();
                AbstractTask task;
                String name, path;
                long delay = 6000;
                ModuleNode temp;

                for (String key : keys) {
                        temp = node.getNode(key);
                        name = temp.getAttribute(NAME_TAG);
                        if(name==null){
                                this.logger.warning("Error retrieving a without name. Name is completely necessary");
                                break;
                        }

                        try{
                                delay = Long.parseLong(temp.getAttribute(DELAY_TAG));
                                if(delay<60000){
                                        delay = 60000;
                                        this.logger.warning("Task delay '"+name+"' increased to 60000 (minimum delay)");
                                }
                        }
                        catch(NumberFormatException e){
                                this.logger.warning("Error retieving the task '"+name+"'. It has an invalid delay (it must be an integer number greater than zero, expressed in millis)");
                                break;
                        }

                        path = temp.getAttribute(PATH_TAG);
                        if(path == null){
                                this.logger.warning("Error retrievin the task '"+name+"'. It has not a task path");
                                break;
                        }

                        try {
                                task = (AbstractTask)Class.forName(path).getConstructors()[0].newInstance(name, delay);

                                temp = temp.getNode(ARGS_TAG);
                                if(temp != null){
                                        if(temp.getAttributes().size()>0){
                                                ModuleNode arg = new ModuleNode(ARG_TAG);
                                                Set<String> argkeys = temp.getAttributes().keySet();
                                                for (String argKey : argkeys) {
                                                        arg.add(argKey, temp.getAttribute(argKey));
                                                }

                                                temp.add(arg.getName(), arg);
                                        }

                                        Set<String> argkeys = temp.getNodes().keySet();
                                        for (String argkey : argkeys) {
                                                ModuleNode argument = temp.getNode(argkey);
                                                task.addArgument(argument.getAttribute(ARG_NAME_TAG), argument.getAttribute(ARG_VALUE_TAG));
                                        }
                                }

                                this.tasks.put(task.getName(), task);
                        } catch (IllegalArgumentException e) {
                                this.logger.warning("Error creating the task '"+name+"'. The constructor receives different parameters");
                                break;
                        } catch (SecurityException e) {
                                this.logger.warning("Error creating the task '"+name+"'.\n"+e.getMessage());
                                break;
                        } catch (InstantiationException e) {
                                this.logger.warning("Error creating the task '"+name+"'. This class must be instantiable (not abstract, interface ...)");
                                break;
                        } catch (IllegalAccessException e) {
                                this.logger.warning("Error creating the task '"+name+"'. This class needs a public constructor and it must be an instantiable class");
                                break;
                        } catch (InvocationTargetException e) {
                                this.logger.warning("Error creating the task '"+name+"'.\n"+e.getMessage());
                                break;
                        } catch (ClassNotFoundException e) {
                                this.logger.warning("Error creating the task '"+name+"'. The class '"+path+"' is missing");
                                break;
                        }
                }
        }

        public AbstractTask getTask(String name){
                return this.tasks.get(name);
        }

        public void beginTasks(){
                Collection<AbstractTask> tasks = this.tasks.values();
                for (AbstractTask task : tasks) {
                        this.beginTask(task);
                }
        }

        public void beginTask(AbstractTask task){
                this.logger.info("Launching task: "+task.getName());
                task.start();
        }

        public void stopTasks(){
                Collection<AbstractTask> tasks = this.tasks.values();
                for (AbstractTask task : tasks) {
                        this.stopTask(task);
                }
        }

        public void stopTask(AbstractTask task){
                this.logger.info("Stopping task: "+task.getName());
                task.stopTask();
        }
}